package com.collabscm.waf.domain.repository.support;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

import com.collabscm.waf.domain.repository.DatabaseRepository;
import com.collabscm.waf.domain.repository.QueryCache;
import com.collabscm.waf.domain.repository.QueryFillerSelector;
import com.collabscm.waf.solution.logging.Logger;
import com.collabscm.waf.solution.logging.LoggerFactory;
import com.collabscm.waf.solution.profiler.Monitor;
import com.collabscm.waf.util.ObjectUtils;
import com.collabscm.waf.vo.PageObject;
import com.collabscm.waf.vo.QueryParameters;

/**
 * 业务对象的数据访问部分，此与DomainService是对应的。
 * 注意：Repository所有数据库的查询语句都放到此处，包括: hql等。
 * 
 * @param <T>  实体类
 * @param <PK> 主键类
 * @author Zeng Raoxing
 */
public class DomainRepository<T, PK extends Serializable> implements DatabaseRepository<T, PK> {
	/**
	 * EntityManager实例
	 */
	protected EntityManager entityManager;
	/**
	 * 当前对应的实体类的Class对象
	 */
	private Class<T> persistentClass;
	/**
	 * 日志对象
	 */
	protected Logger logger = LoggerFactory.getLogger(this.getClass());
	/**
	 * 查询的PageObject是否需要进行缓存
	 */
	private boolean queryCacheable;
	private String queryQL;
	private String queryCountQL;
	
	/**
	 * 子类调用的初始化方法，默认是不进行查询缓存的
	 */
	public DomainRepository() {
		this(false);
	}
	
	/**
	 * 对于不常使用的功能，例如：基础数据，是没有必要缓存的
	 * @param queryCacheable
	 */
	@SuppressWarnings("unchecked")
	public DomainRepository(boolean queryCacheable) {
		this.persistentClass = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
		this.initQL(queryCacheable);
	}
	
	/**
	 * 默认不进行PageObject对象的缓存
	 * @param clazz
	 */
	public DomainRepository(Class<T> clazz) {
		this(clazz, false);
	}
	
	public DomainRepository(Class<T> clazz, boolean queryCacheable) {
		this.persistentClass = clazz;
		this.initQL(queryCacheable);
	}
	
	private void initQL(boolean queryCacheable) {
		this.queryCacheable = queryCacheable;
		if(queryCacheable) {
			queryQL = "select * from " + persistentClass.getName();
			queryCountQL = "select count(*) from " + persistentClass.getName();
		}
	}
	
	@PersistenceContext
	public void setEntityManager(EntityManager entityManager) {
		this.entityManager = entityManager;
	}
	
	/**
	 * 获得映射到的Class
	 */
	public Class<T> getMappingClass() {
		return persistentClass;
	}
	
	@Override
	@Monitor(uptime=100)
	public void create(T newInstance) {
		//关于：persist与merge的区别与相同点，请参见：
		//http://code.google.com/p/debugging/wiki/JPA_persist_merge?ts=1251957814&updated=JPA_persist_merge
		entityManager.persist(newInstance);
		updateCache();
	}

	@Override
	@Monitor(uptime=100)
	public void delete(T persistentObject) {
		entityManager.remove(persistentObject);
		updateCache();
		if(logger.isDebugEnabled()) {
			logger.debug("从数据库中直接删除了" + ObjectUtils.toString(persistentObject));
		}
	}

	@Override
	@Monitor(uptime=100)
	public void update(T transientObject) {
		entityManager.merge(transientObject);
		updateCache();
	}

	@Override
	@Monitor(uptime=100)
	public void deleteById(PK id) {
		//注意：getReference不会做真正的select，因此可以提升性能。
		entityManager.remove(entityManager.getReference(persistentClass, id));
		updateCache();
	}
	
	private void updateCache() {
		if(queryCacheable) {
			QueryCache.removeCache(persistentClass);
		}
	}

	@SuppressWarnings("unchecked")
	@Override
	@Monitor(uptime=500)
	public List<T> findNamedQuery(String namedQuery) {
		Query query = entityManager.createNamedQuery(namedQuery);
		return query.getResultList();
	}

	@Override
	@Monitor(uptime=100)
	public T find(PK id) {
		return entityManager.find(persistentClass, id);
	}
	
//	TODO 待处理其他带条件的单个查询。
//	public T find(PK id, QueryParameters queryParameters) {
//		
//	}

	@SuppressWarnings("unchecked")
	@Override
	@Monitor(uptime=500)
	public List<T> findNamedQuery(String namedQuery,
			QueryParameters queryParameters) {
		Query query = entityManager.createNamedQuery(namedQuery);
		QueryFillerSelector.fill(query, queryParameters);
		return query.getResultList();
	}

	@Override
	@Monitor(uptime=200)
	public long findNamedQueryCount(String namedQuery) {
		return (Long) entityManager.createNamedQuery(namedQuery).getSingleResult();
	}

	@Override
	@Monitor(uptime=500)
	public long findNamedQueryCount(String namedQuery, QueryParameters queryParameters) {
		Query query = entityManager.createNamedQuery(namedQuery);
		QueryFillerSelector.fill(query, queryParameters);
		return (Long) query.getSingleResult();
	}

	//查询PageObject对象，此查询是集成了QueryCache，即如果之前有人已经查过，并且短期内未改变，那么系统将自动从缓存中奖结果取出
	
	@Override
	@Monitor(uptime=500)
	public PageObject<T> findPageObject(QueryParameters queryParameters) {
		Query query = entityManager.createQuery(queryQL);
		Query queryCount = entityManager.createQuery(queryCountQL);
		return findPageObject(null, query, queryCount, queryParameters);
	}

	@Override
	@Monitor(uptime=500)
	public PageObject<T> findPageObjectByNamedQuery(String namedQuery, String countNamedQuery, QueryParameters queryParameters) {
		Query query = entityManager.createNamedQuery(namedQuery);
		Query queryCount = entityManager.createNamedQuery(countNamedQuery);
		return findPageObject(namedQuery, query, queryCount, queryParameters);
	}

	@SuppressWarnings("unchecked")
	protected PageObject<T> findPageObject(String namedQuery, Query query, Query queryCount, QueryParameters queryParameters) {
		PageObject<T> pageObject = (PageObject<T>) QueryCache.findPageObject(persistentClass, namedQuery, queryParameters);
		if(pageObject == null) {
			pageObject = new PageObject<T>(queryParameters);
			QueryFillerSelector.fill(query, queryParameters);
			pageObject.setItems(query.getResultList());
			QueryFillerSelector.fillCountQuery(queryCount, queryParameters);
			pageObject.setTotalSize((Integer)queryCount.getSingleResult());
			QueryCache.putPageObject(persistentClass, namedQuery, queryParameters, pageObject);
			logger.debug("未在缓存中查找到{}+{}+{}的缓存信息。", persistentClass, namedQuery, queryParameters);
		}
		return pageObject;
	}
	
}
